import org.junit.Test;

import java.io.*;

/**
 * Input/Output, I/O技术是非常实用的技术，用于处理设备之间的数据传输。
 * Input：读取外部数据到程序（内存）中。
 * Output：将程序（内存）数据输出到磁盘等存储设备中。
 * |--- |   输入流         |-----|
 * | 数 |---------------->| 程序 |
 * | 据 |                 | 内存 |
 * | 源 |   输出流         |     |
 * |--- |<----------------|-----|
 * <p>
 * 存储单位的不同： 字节流、字符流
 * <p>
 * 基础IO流的框架：
 * 抽象基类        4个节点流(也成为文件流)    4个缓冲流(处理流的一种)
 * InputStream   FileInputStream        BufferedInputStream
 * OutputStream  FileOutputStream       BufferedOutputStream
 * Reader        FileReader             BufferedFileReader
 * Writer        FileWriter             BufferedFileWriter
 * <p>
 * 对于字符流，只能用来操作文本文件，不能用来处理非文本文件。
 * 对于字节流，通常是用来处理非文本文件。但是，如果涉及到文本文件的复制操作，也可以使用字节流。
 * <p>
 * 缓冲流
 * 提升文件读写的效率
 * <p>
 * 打印流
 * PrintWriter  PrintStream
 */
public class IOTest {
    /**
     * 读取文件内容，显示在控制台
     */
    @Test
    public void testFileReader() {
        final int BUFFERSIZE = 200;
        int chIndex;
        int len;
        char[] cBuffer = new char[BUFFERSIZE];
        // 1. 创建File类的对象
        File file = new File("tst.txt");

        // 2. 创建输入型字符流，用于读取数据。
        try (FileReader fileReader = new FileReader(file) /* 包括流资源关闭步骤 */) {
            // 3. 读取数据
            long l = System.currentTimeMillis();
//            // 方式1：8
            while ((chIndex = fileReader.read()) != -1) {
                System.out.print(((char) chIndex));
            }
            // 方式2：7
//            while ((len = fileReader.read(cBuffer)) != -1) {
//                for (int i = 0; i < len; i++) {
//                    System.out.print(cBuffer[i]);
//                }
//            }
            System.out.println(System.currentTimeMillis() - l);

            // 4. 流资源的关闭操作（必须关闭，否则内存泄漏）
            fileReader.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 将内存中的数据写出到指定的文件中
     */
    @Test
    public void testWriter() throws IOException {
        // 1. 创建File类的对象
        File file = new File("write.txt");
//        if (!file.exists())
//            if (!file.createNewFile()) {
//                throw new FileNotFoundException("文件创建失败。");
//            }

        // 2. 创建输出流, 通过构造器指明是否追加
        // 此过程会自动创建不存在的文件
        try (FileWriter fw = new FileWriter(file, true)) {

            // 3. 步骤
            fw.write("aaaa");
            // 4. 关闭资源
            fw.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 复制文件
     */
    @Test
    public void testCopyFile() {
        File file = new File("IO流.png");
        File fileCopy = new File("IO流-copy.png");
        char[] cBuffer = new char[1024];
        int len;
        try (FileReader fr = new FileReader(file);
             FileWriter fw = new FileWriter(fileCopy)) {
            while ((len = fr.read(cBuffer)) != -1) {
                fw.write(cBuffer, 0, len);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 可以使用字节流复制图像、音频文件，以及文本文件。
     */
    @Test
    public void testFileInputStreamAndFileOutputStream() {
        File file = new File("src\\IO流.png");
        File dsttFile = new File("IO流-copy.png");
        byte[] bytes = new byte[100];
        int len;
        try (FileInputStream fileInputStream = new FileInputStream(file);
             FileOutputStream fileOutputStream = new FileOutputStream(dsttFile)) {
            while ((len = fileInputStream.read(bytes)) != -1) {
                fileOutputStream.write(bytes, 0, len);
//                fileOutputStream.flush();
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Test
    public void testBufferedStream() {
        // 1. 创建相关的File类和节点流对象
        File file = new File("src\\IO流.png");
        File dsttFile = new File("IO流-copy.png");
        byte[] bytes = new byte[100];
        int len;
        FileInputStream fileInputStream;
        FileOutputStream fileOutputStream;
        BufferedInputStream bufferedInputStream = null;
        BufferedOutputStream bufferedOutputStream = null;
        try {
            fileInputStream = new FileInputStream(file);
            fileOutputStream = new FileOutputStream(dsttFile);
            bufferedInputStream = new BufferedInputStream(fileInputStream);
            bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
            while ((len = bufferedInputStream.read(bytes)) != -1) {
                bufferedOutputStream.write(bytes, 0, len);
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            // 关闭资源
            // 由于外层流的关闭，也会自动对内层流进行关闭操作。可以省略内层流的关闭。
            try {
                assert bufferedOutputStream != null;
                bufferedOutputStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            try {
                bufferedInputStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Test
    public void testBuffered() {
        String str;
        try (FileReader fileReader = new FileReader("tst.txt");
             BufferedReader bufferedReader = new BufferedReader(fileReader);
             FileWriter fileWriter = new FileWriter("tst_buffered-copy.txt");
             BufferedWriter bufferedWriter = new BufferedWriter(fileWriter)) {

//            int len;
//            char[] cBuffer = new char[1024];
//            while ((len = bufferedReader.read(cBuffer)) != -1) {
//                System.out.print(new String(cBuffer, 0, len));
//            }
            while ((str = bufferedReader.readLine()) != null) {
//                System.out.println(str);
                bufferedWriter.write(str);
                bufferedWriter.newLine();
                // bufferedWriter.flush(); // 刷新的方法。
                // 每当调用此方法，就会主动将内存中的数据写出到磁盘文件中。
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}

